﻿using System;
using System.Threading;

namespace Core.Threading.Locks
{
    public sealed class LockSlim
    {
        private readonly ReaderWriterLockSlim _lockSlim = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

        public IDisposable ReadLock()
        {
            return new ReadLockSlim(_lockSlim);
        }

        public IDisposable WriteLock()
        {
            return new WriteLockSlim(_lockSlim);
        }

        public IDisposable UpgradeableReadLock()
        {
            return new UpgradeableReadLockSlim(_lockSlim);
        }

        #region Nested type: ReadLockSlim

        private class ReadLockSlim : IDisposable
        {
            private readonly ReaderWriterLockSlim _lockSlim;
            private int _isDisposed;

            public ReadLockSlim(ReaderWriterLockSlim lockSlim)
            {
                _lockSlim = lockSlim;
                lockSlim.EnterReadLock();
            }

            #region IDisposable Members

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            #endregion

            private void Dispose(bool disposing)
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    _lockSlim.ExitReadLock();
                }
            }
        }

        #endregion

        #region Nested type: UpgradeableReadLockSlim

        private class UpgradeableReadLockSlim : IDisposable
        {
            private readonly ReaderWriterLockSlim _lockSlim;
            private int _isDisposed;

            public UpgradeableReadLockSlim(ReaderWriterLockSlim lockSlim)
            {
                _lockSlim = lockSlim;
                _lockSlim.EnterUpgradeableReadLock();
            }

            #region IDisposable Members

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            #endregion

            private void Dispose(bool disposing)
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    _lockSlim.ExitUpgradeableReadLock();
                }
            }
        }

        #endregion

        #region Nested type: WriteLockSlim

        private class WriteLockSlim : IDisposable
        {
            private readonly ReaderWriterLockSlim _lockSlim;
            private int _isDisposed;

            public WriteLockSlim(ReaderWriterLockSlim lockSlim)
            {
                _lockSlim = lockSlim;
                lockSlim.EnterWriteLock();
            }

            #region IDisposable Members

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            #endregion

            private void Dispose(bool disposing)
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    _lockSlim.ExitWriteLock();
                }
            }
        }

        #endregion
    }
}